namespace Brjnk.Common
{
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;

    /// <summary>
    /// </summary>
    public class InheritableValues : INotifyPropertyChanged
    {
        private static Dictionary<string, object> defaultValues = new Dictionary<string, object>();

        protected static void DefineInheritableValue(string name, object defaultValue)
        {
            defaultValues.Add(name, defaultValue);
        }

        private InheritableValues parent;

        private Dictionary<string, object> values = new Dictionary<string, object>();

        private WeakEventListener listener;

        public InheritableValues(InheritableValues parent)
        {
            if (parent == null) throw new ArgumentNullException("parent");
            this.parent = parent;
            listener = new WeakEventListener(ReceiveEvent);
            foreach (var name in defaultValues.Keys)
                PropertyChangedEventManager.AddListener(parent, listener, name);
        }

        public InheritableValues()
        {
        }

        private bool ReceiveEvent(Type managerType, object sender, EventArgs e)
        {
            var args = (PropertyChangedEventArgs)e;
            OnPropertyChanged(args.PropertyName);
            return true;
        }

        protected virtual void ProcessPropertyChange(string propertyName) { }

        protected T GetValue<T>(string name)
        {
            if (values.ContainsKey(name)) return (T)values[name];
            if ((object)parent != null) return parent.GetValue<T>(name);
            else return (T)defaultValues[name];
        }

        protected void ClearLocalValue(string name)
        {
            values.Remove(name);
            OnPropertyChanged(name);
        }

        protected void SetValue(string name, object value)
        {
            values[name] = value;
            OnPropertyChanged(name);
        }

        protected bool IsLocalGetter(string propertyName)
        {
            return values.ContainsKey(propertyName) || (object)parent == null;
        }

        protected void IsLocalSetter(string propertyName, bool value)
        {
            if (value && !values.ContainsKey(propertyName)) values.Add(propertyName, GetValue<object>(propertyName));
            else values.Remove(propertyName);

            OnPropertyChanged(propertyName);
            OnPropertyChanged("Is" + propertyName + "Local");
        }

        public event PropertyChangedEventHandler PropertyChanged;

        protected void OnPropertyChanged(string propertyName)
        {
            ProcessPropertyChange(propertyName);
            if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}